/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.backends.jena;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Laurent WOUTERS
 */
public class JenaRuleTranslator {
    private static final String genVarPrefix = "_link";

    protected JenaRepository repository;

    public JenaRuleTranslator(JenaRepository repo) {
        repository = repo;
    }

    public com.hp.hpl.jena.reasoner.rulesys.Rule translate(xowl.interpreter.impl.Evaluator evaluator, xowl.lang.rules.Rule rule) {
        Job job = new Job(evaluator, rule);
        return job.execute();
    }

    protected class Job {
        private xowl.interpreter.impl.Evaluator evaluator;
        private xowl.lang.rules.Rule original;
        private xowl.interpreter.backends.rdf.TranslatorContext context;
        private Map<xowl.interpreter.backends.rdf.XOWLVariableNode, com.hp.hpl.jena.graph.Node> dictJenaVars;
        private Map<com.hp.hpl.jena.graph.Node, xowl.interpreter.backends.rdf.XOWLVariableNode> dictXOWLVars;
        private List<com.hp.hpl.jena.reasoner.rulesys.ClauseEntry> antecedents;
        private List<com.hp.hpl.jena.reasoner.rulesys.ClauseEntry> consequents;
        private String name;
        private Integer genVarCount;

        public Job(xowl.interpreter.impl.Evaluator evaluator, xowl.lang.rules.Rule original) {
            this.evaluator = evaluator;
            this.original = original;
            this.context = new xowl.interpreter.backends.rdf.TranslatorContext();
            this.dictJenaVars = new HashMap<xowl.interpreter.backends.rdf.XOWLVariableNode, com.hp.hpl.jena.graph.Node>();
            this.dictXOWLVars = new HashMap<com.hp.hpl.jena.graph.Node, xowl.interpreter.backends.rdf.XOWLVariableNode>();
            this.antecedents = new ArrayList<com.hp.hpl.jena.reasoner.rulesys.ClauseEntry>();
            this.consequents = new ArrayList<com.hp.hpl.jena.reasoner.rulesys.ClauseEntry>();
            this.name = original.getHasIRI().getHasValue();
            this.genVarCount = 0;
        }

        public com.hp.hpl.jena.reasoner.rulesys.Rule execute() {
            for (xowl.lang.rules.Assertion assertion : original.getAllAntecedents())
                if (assertion.getIsPositive() && !assertion.getIsMeta())
                    translate_axioms(antecedents, assertion.getAllAxioms());
            for (xowl.lang.rules.Assertion assertion : original.getAllConsequents())
                if (assertion.getIsPositive() && !assertion.getIsMeta())
                    translate_axioms(consequents, assertion.getAllAxioms());
            for (xowl.lang.owl2.LiteralExpression guard : original.getAllGuards())
                antecedents.add(translate_condition(guard));
            com.hp.hpl.jena.reasoner.rulesys.Rule result = new com.hp.hpl.jena.reasoner.rulesys.Rule(name, consequents, antecedents);
            result.setNumVars(dictJenaVars.size());
            return result;
        }

        private void translate_axioms(List<com.hp.hpl.jena.reasoner.rulesys.ClauseEntry> buffer, Collection<xowl.lang.owl2.Axiom> axioms) {
            try {
                xowl.interpreter.backends.rdf.Translation t1 = repository.getXOWLTranslator().translateAxioms(evaluator, context, false, false, axioms);
                if (buffer == antecedents) {
                    for (xowl.interpreter.backends.rdf.RDFTriple triple : t1.getTriples())
                        buffer.add(translate_triple(triple));
                } else {
                    for (xowl.interpreter.backends.rdf.RDFTriple triple : t1.getTriples()) {
                        if (triple.getSubject().getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Dynamic)
                            buffer.add(translate_dyn_triple(triple));
                        else if (triple.getProperty().getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Dynamic)
                            buffer.add(translate_dyn_triple(triple));
                        else if (triple.getObject().getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Dynamic)
                            buffer.add(translate_dyn_triple(triple));
                        else
                            buffer.add(translate_triple(triple));
                    }
                }
            } catch (xowl.interpreter.XOWLException ex) { repository.getLog().error(JenaRepository.name, ex.getHeadline(), ex); }
        }
        private com.hp.hpl.jena.reasoner.TriplePattern translate_triple(xowl.interpreter.backends.rdf.RDFTriple triple) {
            com.hp.hpl.jena.graph.Node subject = translate_subject(triple.getSubject());
            com.hp.hpl.jena.graph.Node prop = translate_property(triple.getProperty());
            com.hp.hpl.jena.graph.Node object = translate_object(triple.getObject());
            return new com.hp.hpl.jena.reasoner.TriplePattern(subject, prop, object);
        }
        private com.hp.hpl.jena.graph.Node translate_subject(xowl.interpreter.backends.rdf.RDFSubjectNode node) {
            if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.IRIReference)
                return repository.getJenaResource((xowl.interpreter.backends.rdf.RDFIRIReference)node).asNode();
            else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Blank || node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Anonymous)
                return repository.getJenaResource((xowl.interpreter.backends.rdf.RDFBlankNode)node).asNode();
            else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Variable)
                return translate_var((xowl.interpreter.backends.rdf.XOWLVariableNode)node);
            return translate_dyn((xowl.interpreter.backends.rdf.XOWLDynamicNode)node);
        }
        private com.hp.hpl.jena.graph.Node translate_property(xowl.interpreter.backends.rdf.RDFProperty node) {
            if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.IRIReference)
                return repository.getJenaResource((xowl.interpreter.backends.rdf.RDFIRIReference)node).asNode();
            else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Variable)
                return translate_var((xowl.interpreter.backends.rdf.XOWLVariableNode)node);
            return translate_dyn((xowl.interpreter.backends.rdf.XOWLDynamicNode)node);
        }
        private com.hp.hpl.jena.graph.Node translate_object(xowl.interpreter.backends.rdf.RDFNode node) {
            if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.IRIReference)
                return repository.getJenaResource((xowl.interpreter.backends.rdf.RDFIRIReference)node).asNode();
            else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Blank || node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Anonymous)
                return repository.getJenaResource((xowl.interpreter.backends.rdf.RDFBlankNode)node).asNode();
            else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Literal)
                return repository.getJenaLiteral((xowl.interpreter.backends.rdf.RDFLiteralNode)node).asNode();
            else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Variable)
                return translate_var((xowl.interpreter.backends.rdf.XOWLVariableNode)node);
            return translate_dyn((xowl.interpreter.backends.rdf.XOWLDynamicNode)node);
        }
        private com.hp.hpl.jena.graph.Node translate_var(xowl.interpreter.backends.rdf.XOWLVariableNode node) {
            if (dictJenaVars.containsKey(node))
                return dictJenaVars.get(node);
            com.hp.hpl.jena.reasoner.rulesys.Node_RuleVariable var = new com.hp.hpl.jena.reasoner.rulesys.Node_RuleVariable(node.getVariable().getName(), dictJenaVars.size() + genVarCount);
            dictJenaVars.put(node, var);
            dictXOWLVars.put(var, node);
            return var;
        }
        private com.hp.hpl.jena.graph.Node translate_dyn(xowl.interpreter.backends.rdf.XOWLDynamicNode node) {
            String varName = genVarPrefix + genVarCount.toString();
            com.hp.hpl.jena.reasoner.rulesys.Node_RuleVariable jVar = new com.hp.hpl.jena.reasoner.rulesys.Node_RuleVariable(varName, dictJenaVars.size() + genVarCount);
            genVarCount++;
            
            xowl.lang.actions.QueryVariable xVar = new xowl.lang.actions.QueryVariable();
            xVar.setName(varName);
            xowl.lang.actions.Equal condition = new xowl.lang.actions.Equal();
            condition.setLhs(xVar);
            condition.setRhs(node.getDynamicExpression());
            
            JenaRuleCondition builtin = new JenaRuleCondition(repository, condition, dictJenaVars);
            builtin.addVariable(xVar, jVar);
            List<com.hp.hpl.jena.graph.Node> params = new ArrayList<com.hp.hpl.jena.graph.Node>();
            params.addAll(dictJenaVars.values());
            params.add(jVar);
            com.hp.hpl.jena.reasoner.rulesys.Functor functor = new com.hp.hpl.jena.reasoner.rulesys.Functor(builtin.getName(), params);
            functor.setImplementor(builtin);
            antecedents.add(functor);
            return jVar;
        }
        
        private com.hp.hpl.jena.reasoner.rulesys.ClauseEntry translate_condition(xowl.lang.owl2.LiteralExpression condition) {
            JenaRuleCondition builtin = new JenaRuleCondition(repository, condition, dictJenaVars);
            List<com.hp.hpl.jena.graph.Node> params = new ArrayList<com.hp.hpl.jena.graph.Node>();
            params.addAll(dictJenaVars.values());
            com.hp.hpl.jena.reasoner.rulesys.Functor functor = new com.hp.hpl.jena.reasoner.rulesys.Functor(builtin.getName(), params);
            functor.setImplementor(builtin);
            return functor;
        }
        
        private com.hp.hpl.jena.reasoner.rulesys.ClauseEntry translate_dyn_triple(xowl.interpreter.backends.rdf.RDFTriple triple) {
            JenaRuleDynHead builtin = new JenaRuleDynHead(repository, dictJenaVars);
            if (triple.getSubject().getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Dynamic)
                builtin.setSubject(((xowl.interpreter.backends.rdf.XOWLDynamicNode)triple.getSubject()).getDynamicExpression());
            else
                builtin.setSubject(translate_subject(triple.getSubject()));
            if (triple.getProperty().getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Dynamic)
                builtin.setProperty(((xowl.interpreter.backends.rdf.XOWLDynamicNode)triple.getProperty()).getDynamicExpression());
            else
                builtin.setProperty(translate_property(triple.getProperty()));
            if (triple.getObject().getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Dynamic)
                builtin.setObject(((xowl.interpreter.backends.rdf.XOWLDynamicNode)triple.getObject()).getDynamicExpression());
            else
                builtin.setObject(translate_object(triple.getObject()));
            
            List<com.hp.hpl.jena.graph.Node> params = new ArrayList<com.hp.hpl.jena.graph.Node>();
            params.addAll(dictJenaVars.values());
            com.hp.hpl.jena.reasoner.rulesys.Functor functor = new com.hp.hpl.jena.reasoner.rulesys.Functor(builtin.getName(), params);
            functor.setImplementor(builtin);
            return functor;
        }
    }
}
